package aiP.A3.Client.Dispatcher;

import aiP.A3.AuftragKomponente.DataAccessLayer.Entity.Angebot;
import aiP.A3.AuftragKomponente.DataAccessLayer.Entity.Auftrag;
import aiP.A3.BuchhaltungKomponente.DataAccessLayer.Entity.Kunde;
import aiP.A3.BuchhaltungKomponente.DataAccessLayer.Entity.Rechnung;
import aiP.A3.BuchhaltungKomponente.DataAccessLayer.Entity.Zahlungseingang;
import aiP.A3.Client.Monitor.Instance;
import aiP.A3.Client.Monitor.Monitor;
import aiP.A3.FertigungKomponente.DataAccessLayer.Entity.Bauteil;
import aiP.A3.MPS.Interfaces.IServerInstance;
import aiP.A3.PersistenceService.iEntity;
import aiP.A3.VersandKomponente.DataAccessLayer.DTO.TransportauftragDTO;
import aiP.A3.VersandKomponente.DataAccessLayer.Entity.Transportauftrag;

import java.io.IOException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: Andreas
 * Date: 15.05.14
 * Time: 14:67
 * To change this template use File | Settings | File Templates.
 */


public class Dispatcher implements IDispatcher {

    private int callcount = 0;                                                  // Anzahl der Anfragen
    private Date now;                                                           // Zeitstempel für Log Ausgaben
    private SimpleDateFormat dateFormatter = new SimpleDateFormat("hh:mm:ss");  // Zeitstempel formatieren
    private Monitor monitor;

    public Dispatcher(Monitor aMonitor) {
        monitor = aMonitor;
    }

    @Override
    public Map<String, Kunde> getKundenMap() throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: Get Kunden");
        Collection<iEntity> col = getNextInstance().getAllCustomer();

        Map<String, Kunde> result = new HashMap<String, Kunde>();
        for (iEntity elem : col) {
            if (elem instanceof Kunde) {
                Kunde tKunde = (Kunde) elem;
                result.put(tKunde.getName(), tKunde);
            }
        }
        return result;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Map<String, Bauteil> getBauteilMap() throws RemoteException, NotBoundException {
        // Muss noch später auf dem Server angepasst werden
        //  Collection<iEntity> col = Persist.getInstance().load(iEntity.BAUTEIL);

        now = new Date();
        Collection<iEntity> col = getNextInstance().getAllBauteile();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: Get Bauteile");

        Map<String, Bauteil> result = new HashMap<String, Bauteil>();
        for (iEntity elem : col) {
            if (elem instanceof Bauteil) {
                Bauteil tBauteil = (Bauteil) elem;
                result.put(tBauteil.getName(), tBauteil);
            }
        }
        return result;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Map<Integer, Kunde> getKundenMapByID() throws NotBoundException, RemoteException {
        // Muss noch später auf dem Server angepasst werden
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: Get Kunden");
        Collection<iEntity> col = getNextInstance().getAllCustomer();//Persist.getInstance().load(iEntity.KUNDE);

        Map<Integer, Kunde> result = new HashMap<Integer, Kunde>();
        for (iEntity elem : col) {
            if (elem instanceof Kunde) {
                Kunde tKunde = (Kunde) elem;
                result.put(tKunde.getKundeId(), tKunde);
            }
        }
        return result; //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Map<Integer, Bauteil> getBautei_Id() throws NotBoundException, RemoteException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: Get Bauteile");
        Collection<iEntity> col = getNextInstance().getAllBauteile(); // Persist.getInstance().load(iEntity.BAUTEIL);
        Map<Integer, Bauteil> result = new HashMap<Integer, Bauteil>();
        for (iEntity elem : col) {
            if (elem instanceof Bauteil) {
                Bauteil tBauteil = (Bauteil) elem;
                result.put(tBauteil.getBauteilId(), tBauteil);
            }
        }
        return result;  //To change body of implemented methods use File | Settings | File Templates.
    }


    public Map<Integer, Set<Angebot>> getAngebotMap(Kunde aKunde) throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: Get Angebote");
        Collection<iEntity> col = getNextInstance().getAllAngebote(); //Persist.getInstance().load(iEntity.ANGEBOT);

        Map<Integer, Set<Angebot>> result = new HashMap<Integer, Set<Angebot>>();
        for (iEntity elem : col) {
            if (elem instanceof Angebot) {
                Angebot tAngebot = (Angebot) elem;
                if (result.containsKey(tAngebot.getAngebotsnr())) {
                    result.get(tAngebot.getAngebotsnr()).add(tAngebot);
                } else {
                    Set<Angebot> tAngebotSet = new HashSet<Angebot>();
                    tAngebotSet.add(tAngebot);
                    result.put(tAngebot.getAngebotsnr(), tAngebotSet);
                }
            }
        }
        return result;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Map<Integer, Auftrag> getAuftragsMap() throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: getAuftragsMap");
        Collection<iEntity> col = getNextInstance().getAllAuftraege();
        Map<Integer, Auftrag> result = new HashMap<Integer, Auftrag>();
        for (iEntity elem : col) {
            if (elem instanceof Auftrag) {
                Auftrag tAuftrag = (Auftrag) elem;
                if (tAuftrag.getIstAbgeschlossen() == 0)
                    result.put(tAuftrag.getAuftragsnr(), tAuftrag);
            }
        }
        return result;
    }

    @Override
    public Collection<Angebot> getAngebot(int AngebotID) throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: getAnbebot by ID");
        Collection<Angebot> result = new HashSet<Angebot>();
        for (iEntity elem : getNextInstance().getAllAngebote()) {
            if (elem instanceof Angebot) {
                Angebot tAngebot = (Angebot) elem;
                if (AngebotID == tAngebot.getAngebotsnr())
                    result.add(tAngebot);
            }
        }
        return result;
    }

    @Override
    public Collection<Angebot> getAllAngebote() throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: getAllAngebote");
        Collection<Angebot> collection = new HashSet<Angebot>();
        for (iEntity elem : getNextInstance().getAllAngebote()) {
            if (elem instanceof Angebot) {
                Angebot tAngebot = (Angebot) elem;
                collection.add(tAngebot);
            }
        }
        return collection;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public void getZahlungseingaengeFromQueue() throws IOException, NotBoundException, InterruptedException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: getAllZahlungseingaenge");
        getNextInstance().getZahlungseingaengeFromQueue();
    }


    public Collection<Zahlungseingang> getAllZahlungseingaenge() throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeite Anfrage: Alle Zahlungseingänge von DB");
        Collection<Zahlungseingang> result = new HashSet<Zahlungseingang>();
        for (iEntity elem: getNextInstance().getAllZahlungseingaenge()){
            if (elem instanceof  Zahlungseingang){
                Zahlungseingang tZahlungseingang = (Zahlungseingang)elem;
                result.add(tZahlungseingang);
            }
        }
        return result;
    }

    @Override
    public Collection<Rechnung> getAllRechnung() throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: getAllRechnung");
        Collection<Rechnung> col = new HashSet<Rechnung>();
        for(iEntity elem: getNextInstance().getAllRechnungen()) {
            if (elem instanceof Rechnung)  {
                Rechnung tRechnung = (Rechnung)elem;
                col.add(tRechnung);
            }
        }
        return col;
    }

    @Override
    public void saveAuftrag(Auftrag elem) throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + " Bearbeitete Anfrage: Save Auftrag");
        getNextInstance().CreateAuftrag(elem);
    }

    @Override
    public void saveAngebot(Set<Angebot> elem) throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + "Bearbeitete Anfrage: Save Angebot");
        getNextInstance().CreateAngebot(elem);
    }

    @Override
    public void saveRechnung(Rechnung elem) throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + "Bearbeitete Anfrage: Save Rechnung");
        getNextInstance().CreateRechnung(elem);
    }

    @Override
    public void saveTransport(Transportauftrag auftrag) throws RemoteException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + "Bearbeite Anfrage: Save Transportauftrag");
        getNextInstance().createTransportauftrag(auftrag);
    }

    @Override
    public boolean postToVersanddienstleister(Transportauftrag transportauftrag) throws IOException, NotBoundException {
        now = new Date();
        monitor.getDashboard().logMessage(dateFormatter.format(now) + "Bearbeite Anfrage:  SendTo Versanddienstleister");
        return getNextInstance().sendToVersand(transportauftrag);
    }


    /**
     * Such dir eine ServerInstanz aus per Round Robin
     *
     * @return IServerinstanz, auf der die getBauteile usw angewendet werden
     * @throws RemoteException
     * @throws NotBoundException
     */
    private int calling = 0;

    private IServerInstance getNextInstance() throws RemoteException, NotBoundException {
        List<Instance> instances = monitor.getInstanzen();
        if (instances.size() == 0)
            throw new RuntimeException("Keine Instancen vorhanden");
        // get Instanz per Round Robin
        Instance instance = instances.get(this.calling % instances.size());
        IServerInstance result = instance.getStub();
        calling++;
        if (result.isAlive()) {
            this.callcount++;
            now = new Date();
            monitor.getDashboard().logMessage(dateFormatter.format(now) + " Anzahl der Anfragen: " + callcount + ", Instanz: " + instance.getId());
            monitor.getDashboard().printCallcounterTotal(callcount);
            return result;
        } else
            return getNextInstance();
    }

    public String toString() {
        return "Dispatcher";
    }


}